\defmodule {Bin}

A \texttt{Bin} corresponds to a pile of identical tokens, and a list of
processes waiting for the tokens when the list is empty.
It is a producer/consumer process synchronization device.
Tokens can be added to the pile (i.e., {\em produced\/}) by the
method \method{put}{}.
A process can request tokens from the pile (i.e., {\em consume\/})
by calling \method{take}{}.

The behavior of a \texttt{Bin} is somewhat similar to that of a
\class{Resource}.  Each \texttt{Bin} has a single queue of waiting
processes, with FIFO or LIFO service policy, and
which can be accessed via the method \method{waitList}{}.
This list actually contains objects of the class \class{UserRecord}.
Each \class{UserRecord} points to a process
and contains some additional information.

\bigskip\hrule

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{code}
\begin{hide}
/*
 * Class:        Bin
 * Description:  corresponds to a pile of identical tokens, and a list of
                 processes waiting for the tokens when the list is empty
 * Environment:  Java
 * Software:     SSJ 
 * Copyright (C) 2001  Pierre L'Ecuyer and Universite de Montreal
 * Organization: DIRO, Universite de Montreal
 * @author       
 * @since

 * SSJ is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License (GPL) as published by the
 * Free Software Foundation, either version 3 of the License, or
 * any later version.

 * SSJ is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * A copy of the GNU General Public License is available at
   <a href="http://www.gnu.org/licenses">GPL licence site</a>.
 */
\end{hide}
package umontreal.iro.lecuyer.simprocs;
\begin{hide}
import java.util.ListIterator;
import umontreal.iro.lecuyer.simevents.Simulator;
import umontreal.iro.lecuyer.simevents.Accumulate;
import umontreal.iro.lecuyer.simevents.LinkedListStat;
import umontreal.iro.lecuyer.stat.Tally;
import umontreal.iro.lecuyer.util.PrintfFormat;
\end{hide}

public class Bin \begin{hide} {

   private static final int FIFO  = 1;
   private static final int LIFO  = 2;

    // Variables

        private ProcessSimulator sim;
        private String name;
        private int available = 0;
        private int policy = FIFO;
        private LinkedListStat<UserRecord> waitingList;
        private ListIterator<UserRecord> iter;
        private Accumulate statAvail;
        private boolean stats;
        private double     initStatTime;

\end{hide}\end{code}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsubsection* {Constructors}
\begin{code}

   public Bin() \begin{hide} {
      this ("");
   }\end{hide}
\end{code}
 \begin{tabb}  Constructs a new bin, initially empty, with
   service policy FIFO and linked with the default simulator.
 \end{tabb}
\begin{code}

   public Bin(ProcessSimulator sim) \begin{hide} {
      this (sim, "");
   }\end{hide}
\end{code}
 \begin{tabb}  Constructs a new bin, initially empty, with
   service policy FIFO and linked with simulator sim.
 \end{tabb}
\begin{htmlonly}
   \param{sim}{Simulator associated to the current variable.}
\end{htmlonly}
\begin{code}

   public Bin (String name) \begin{hide} {
      try {
         ProcessSimulator.initDefault();
         this.sim = (ProcessSimulator)Simulator.getDefaultSimulator();
         waitingList = new LinkedListStat<UserRecord>(sim);
         this.name = name;
         iter = waitingList.listIterator();
         stats = false;
      }
      catch (ClassCastException e) {
         throw new IllegalArgumentException("Wrong default Simulator type");
      }
   }\end{hide}
\end{code}
 \begin{tabb}  Constructs a new bin, initially empty, with service policy FIFO,
  identifier \texttt{name} and linked with the default simulator.
 \end{tabb}
\begin{htmlonly}
   \param{name}{name associated to the bin}
\end{htmlonly}
\begin{code}

   public Bin (ProcessSimulator sim, String name) \begin{hide} {
      this.sim = sim;
      waitingList = new LinkedListStat<UserRecord>(sim);
      this.name = name;
      iter = waitingList.listIterator();
      stats = false;
   }\end{hide}
\end{code}
 \begin{tabb}  Constructs a new bin, initially empty, with
   service policy FIFO, identifier \texttt{name} and linked with simulator sim.
 \end{tabb}
\begin{htmlonly}
   \param{sim}{Simulator associated to the current variable.}
   \param{name}{name associated to the bin}
\end{htmlonly}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsubsection* {Methods}
\begin{code}

   public void init() \begin{hide} {
      int oldAvail = available;
      waitingList.clear();
      available = 0;
      if (stats) initStat();
   }\end{hide}
\end{code}
 \begin{tabb}  Reinitializes this bin by clearing up its pile of tokens and
   its waiting list.
   The processes in this list (if any) remain in the same states.
 \end{tabb}
\begin{code}

   public void setStatCollecting (boolean b) \begin{hide} {
      if (b) {
         if (stats)
            throw new IllegalStateException ("Already collecting statistics for this resource");
         stats = true;
         waitingList.setStatCollecting (true);
         if (statAvail != null)
            initStat();
         else {
            statAvail = new Accumulate (sim, "StatOnAvail");
            statAvail.update (available);
         }
      }
      else {
         if (stats)
            throw new IllegalStateException ("Not collecting statistics for this resource");
         stats = false;
         waitingList.setStatCollecting (false);
      }
   }\end{hide}
\end{code}
 \begin{tabb}  Starts or stops collecting statistics on the list returned
  by \method{waitList}{} for this bin.
  If the statistical collection is turned ON, It also constructs (if not yet done)
  and initializes an additional statistical
  collector of class
   \externalclass{umontreal.iro.lecuyer.simevents}{Accumulate}
  for this bin.  This collector will be updated
  automatically.  It can be accessed via \method{statOnAvail}{}, and
   monitors the evolution of the
  available tokens of the bin
  as a function of time.
 \end{tabb}
\begin{htmlonly}
   \param{b}{\texttt{true} to turn statistical collection ON, \texttt{false} to turn it OFF}
\end{htmlonly}
\begin{code}

   public void initStat()\begin{hide} {
        if (!stats)  throw new IllegalStateException (
                               "Not collecting statistics for this resource");
        statAvail.init();
        statAvail.update (available);
        waitingList.initStat();
        initStatTime = sim.time();
   }\end{hide}
\end{code}
 \begin{tabb}  Reinitializes all the statistical collectors for this
   bin.  These collectors must exist, i.e.,
   \method{setStatCollecting}{}~\texttt{(true)} must have been invoked earlier for
   this bin.
 \end{tabb}
\begin{code}

   public void setPolicyFIFO()\begin{hide} {
      policy = FIFO;
   }\end{hide}
\end{code}
  \begin{tabb}  Sets the service policy for ordering processes waiting
   for tokens on the bin to FIFO (\emph{first in, first out}):
   the processes are placed in the
   list (and served) according to their order of arrival.
   \end{tabb}
\begin{code}

   public void setPolicyLIFO()\begin{hide} {
      policy = LIFO;
   }\end{hide}
\end{code}
   \begin{tabb}  Sets the service policy for ordering processes waiting
   for tokens on the bin to LIFO (\emph{last in, first out}):
    the processes are placed in the
   list (and served) according to their inverse order of arrival:
   the last arrived are served first.
  \end{tabb}
\begin{code}

   public int getAvailable() \begin{hide} {
      return available;
   }\end{hide}
\end{code}
 \begin{tabb}  Returns the number of available tokens for this bin.
 \end{tabb}
\begin{htmlonly}
   \return{the number of available tokens}
\end{htmlonly}
\begin{code}

   public void take (int n) \begin{hide} {
        SimProcess p = sim.currentProcess();
        if (n <= available) {
            // The process gets the resource right away.
            available -= n;
            if (stats) {
               statAvail.update (available);
               waitingList.statSojourn().add (0.0);
            }
        }
        else {
            // Not enough units of the resource are available.
            // The process joins the queue waitingList;
            UserRecord record = new UserRecord (n, p, sim.time());
            switch (policy) {
                case FIFO : waitingList.addLast (record); break;
                case LIFO : waitingList.addFirst (record); break;
                default   : throw new IllegalStateException (
                                       "the policy must be LIFO or FIFO");
            }
            p.suspend();
        }
   }\end{hide}
\end{code}
 \begin{tabb}  The executing process invoking this method requests
   \texttt{n} tokens from this bin.  If enough tokens are available,
   the number of tokens in the bin is reduced by \texttt{n} and the
   process continues its execution.
   Otherwise, the executing process is placed into the
   \method{waitList}{} list (the queue) for this bin and is suspended
   until it can obtain the requested number of tokens.
 \end{tabb}
\begin{htmlonly}
   \param{n}{number of requested tokens}
\end{htmlonly}
\begin{code}

   public void put (int n) \begin{hide} {
        available+=n;
        if (stats) statAvail.update (available);
        if (waitingList.size()>0)  wakeProcess();
   }

   private void wakeProcess() {

        UserRecord record;
        ListIterator<UserRecord> iter = waitingList.listIterator();
        while (iter.hasNext() && available > 0) {
            record=iter.next();
            if (!record.process.isAlive())
                throw new IllegalStateException(
                           "process not alive");
                // The thread for this process is still alive.

            if (record.numUnits <= available) {
                // This request can now be satisfied
                record.process.resume();
                available -= record.numUnits;
                if (stats) statAvail.update (available);
                iter.remove();
            }
        }
    } \end{hide}
\end{code}
 \begin{tabb}  Adds \texttt{n} tokens to this bin.
  If there are processes waiting for tokens from this bin and
  whose requests can now be satisfied, they obtain the tokens and
  resume their execution.
 \end{tabb}
\begin{htmlonly}
   \param{n}{number of tokens to put into the bin}
\end{htmlonly}
\begin{code}

   public LinkedListStat waitList() \begin{hide} {
      return waitingList;
   }\end{hide}
\end{code}
 \begin{tabb}  Returns the list of \class{UserRecord}
   for the processes waiting for tokens from this bin.
 \end{tabb}
\begin{htmlonly}
   \return{the list of waiting process user records}
\end{htmlonly}
\begin{code}

   public Accumulate statOnAvail() \begin{hide} {
      return statAvail;
   }\end{hide}
\end{code}
 \begin{tabb}  Returns the statistical collector for the available tokens
  on the bin as a function of time.
  This collector exists only if \method{setStatCollecting}{}~\texttt{(true)}
  has been invoked previously.
 \end{tabb}
\begin{htmlonly}
   \return{the probe for available tokens}
\end{htmlonly}
\begin{code}

   public String report() \begin{hide} {

    if (statAvail == null) throw new IllegalStateException ("Asking a report for a bin "
                          +"for which setStatCollecting (true) has not been called");

    Accumulate sizeWait = waitingList.statSize();
    Tally sojWait = waitingList.statSojourn();

    PrintfFormat str = new PrintfFormat();

    str.append ("REPORT ON BIN : ").append (name).append (PrintfFormat.NEWLINE);
    str.append ("   From time : ").append (7, 2, 2, initStatTime);
    str.append ("   to time : ");
    str.append (10,2, 2, sim.time());
    str.append (PrintfFormat.NEWLINE + "                    min        max     average  ");
    str.append ("standard dev.  nb. obs.");

    str.append (PrintfFormat.NEWLINE + "   Available tokens ");
    str.append (8, (int)(statAvail.min()+0.5));
    str.append (11, (int)(statAvail.max()+0.5));
    str.append (12, 3, 2, statAvail.average());

    str.append (PrintfFormat.NEWLINE + "   Queue Size  ");
    str.append (8, (int)(sizeWait.min()+0.5));
    str.append (11, (int)(sizeWait.max()+0.5));
    str.append (12, 3, 2, sizeWait.average());

    str.append (PrintfFormat.NEWLINE + "   Wait    ");
    str.append (12, 3, 2, sojWait.min()).append (' ');
    str.append (10, 3, 2, sojWait.max()).append (' ');
    str.append (11, 3, 2, sojWait.average()).append (' ');
    str.append (10, 3, 2, sojWait.standardDeviation());
    str.append (10, sojWait.numberObs());

    return str.toString();
   }\end{hide}
\end{code}
 \begin{tabb}  Returns a string containing a complete statistical report on
  this  bin. The method \\ \method{setStatCollecting}{}~\texttt{(true)} must have
  been invoked previously, otherwise no statistics will have been collected.
  The report contains statistics on the available tokens, queue size and
  waiting time for this bin.
 \end{tabb}
\begin{htmlonly}
   \return{a statistical report for this bin, represented as a string}
\end{htmlonly}
\begin{code}

   public Simulator simulator() \begin{hide} {
      return sim;
   } \end{hide}
\end{code}
 \begin{tabb}   Returns the current simulator of this continuous-time variable.
 \end{tabb}
\begin{htmlonly}
   \return{the current simulator of the variable}
\end{htmlonly}
\begin{code}

   public void setSimulator (ProcessSimulator sim) \begin{hide} {
      this.sim = sim;
   } \end{hide}
\end{code}
 \begin{tabb}   Set the current simulator of this continuous-time variable.
 This method must not be called while a simulator is running.
 \end{tabb}
\begin{htmlonly}
   \param{sim}{the simulator of the current variable}
\end{htmlonly}
\begin{code}\begin{hide}
}\end{hide}
\end{code}
